package exchange

import (
	"global/core"
	"fmt"
	"time"
	"github.com/ngaut/log"
	"global/libs/structs"
	"encoding/json"
	"strings"
	"global/libs/exchanges"
	"github.com/garyburd/redigo/redis"
	"github.com/shopspring/decimal"
	"math"
	"sync"
)

const (
	HuobiKlineResolution1min = "1min"
)

type HuobiKline struct {
	BaseKline
	wg 			sync.WaitGroup
}

func NewHuobiKline() KlineInterface {
	return new(HuobiKline)
}

func (k *HuobiKline) Init(ctx *core.Context, slug string) error {
	k.BaseKline.init(ctx, slug)
	if err := k.loadSymbols(); err != nil {
		return err
	}
	if len(k.symbols) == 0 {
		return fmt.Errorf("init symbols failed, length 0, slug: %s", slug)
	}
	return nil
}

func (k *HuobiKline) Run() {

	// 10s/100次限制
	symbolLength := len(k.symbols)
	times := int(math.Ceil(float64(symbolLength) / 50))

	go func() {

		defer func() {
			if r := recover(); r != nil {
				log.Errorf("Error: some error happened.")
			}
		}()

		for {
			for i:=0; i<times; i++ {
				start := i * 50
				end := (i + 1) * 50
				if end > symbolLength {
					end = symbolLength
				}

				symbols := k.symbols[start:end]
				k.wg.Add(len(symbols))
				for _, v := range symbols {
					go func(symbol structs.Symbol) {
						defer k.wg.Done()
						k.spider(&symbol)
					}(v)
				}
				k.wg.Wait()
				log.Infof("complete 50 symbols, count: %d", len(symbols))
				time.Sleep(11 * time.Second)
			}
		}
	}()
}

func (k *HuobiKline) spider(symbol *structs.Symbol)  {
	symbolStr := fmt.Sprintf("%s_%s", strings.ToLower(symbol.From), strings.ToLower(symbol.To))
	datas, err := k.get(symbol)
	if err != nil {
		log.Errorf("Error: %s", err)
		return
	}

	if len(datas) == 0 {
		return
	}

	conn := k.ctx.GetBaseRedis().Get()
	defer conn.Close()

	findLastTime, err := core.GetSpiderKlineLastTime(conn, k.slug, "1min", symbolStr)
	if err != nil{
		log.Errorf("Error: error: %s, last time: %d", err, findLastTime)
		return
	}

	for i := len(datas) - 1; i>=0; i-- {
		tx := datas[i].Id
		fx := findLastTime
		if tx < fx { continue }
		if err := k.saveKlineData(&conn, datas[i], symbol, tx != fx); err != nil {
			log.Fatalf("Error: %s", err)
			return
		}
	}

}

// 保存K线数据
func (k *HuobiKline) saveKlineData(conn *redis.Conn,
	kline *exchanges.HuobiKline,
	symbol *structs.Symbol,
	isAdd bool) error {

	data := structs.SpiderKline{
		Exchange: 	k.slug,
		From: 		symbol.From,
		To: 		symbol.To,
		DateTime:	int64(kline.Id),
		Open: 		decimal.NewFromFloat(kline.Open).String(),
		High: 		decimal.NewFromFloat(kline.High).String(),
		Low:	 	decimal.NewFromFloat(kline.Low).String(),
		Close: 		decimal.NewFromFloat(kline.Close).String(),
		Volume: 	decimal.NewFromFloat(kline.Vol).String(),
	}
	return core.SaveKlineDetailToSymbol(*conn, "1min", data, isAdd)
}

// 获取GET数据
func (k *HuobiKline) get(symbol *structs.Symbol) ([]*exchanges.HuobiKline, error) {
	url := core.GetHuobiMarketUrl("/history/kline")
	body, err := k.BaseKline.get(url, map[string]interface{}{
		"size": 300,
		"symbol": fmt.Sprintf("%s%s", strings.ToLower(symbol.From), strings.ToLower(symbol.To)),
		"period": HuobiKlineResolution1min,
	})

	if err != nil {
		return nil, err
	}

	type responseStruct struct {
		Data 	[]*exchanges.HuobiKline		`json:"data"`
	}

	var response responseStruct
	if err := json.Unmarshal(body, &response); err != nil {
		return nil, err
	}

	return response.Data, nil
}
